# !/usr/bin/env python
# -*-coding:utf-8 -*-

"""
# File       : schedule_task_view.py
# Time       ：2023/8/2 15:20
# Author     ：YangYong
# version    ：python 3.10
# Description：
    定时任务视图类
    schedule task
"""
import datetime
import json

from flask import request, current_app, url_for
from flask_restx import fields

from apis.orderlines import orderlines_ns
from apis.orderlines.models import ScheduleTask, Process
from apis.orderlines.schema.schedule_task_schema import ScheduleTaskSchema, CreateScheduleTask
from apis.orderlines.views.orderlines_schedule.schedule_utils import ScheduleUtils
from apis.schedule_plan.models import DatePlan, IntervalPlan, CrontabPlan
from public.api_utils.api_decorator import handle_api_error

from public.base_export_excel_view import BaseExportExcelView
from public.base_model import db
from public.base_response import MarshalModel
from public.base_view import BaseView

schedule_task = {
    'id': fields.String(description='定时任务ID'),
    'schedule_task_name': fields.String(description='计划任务名称'),
    'process_id': fields.String(description='流程ID'),
    'process_name': fields.String(description='流程名称'),
    'version': fields.String(description='流程版本'),
    'process_params': fields.String(description='流程参数'),
    'process_config': fields.String(description='流程其他配置'),
    'desc': fields.String(description='流程描述'),
    'trigger': fields.String(description='调度触发类型'),
    'schedule_plan': fields.String(description='调度计划参数'),
    'schedule_plan_id': fields.String(description='调度计划ID'),
    'invalid_start_time': fields.String(description='失效开始时间'),
    'invalid_end_time': fields.String(description='失效结束时间'),
}

schedule_task_marshal = MarshalModel('schedule_task', orderlines_ns)


class ScheduleTaskView(BaseView):
    url = '/schedule_task'

    def __init__(self, *args, **kwargs):
        super(ScheduleTaskView, self).__init__(*args, **kwargs)
        self.schedule_utils = ScheduleUtils()
        self.table_orm = ScheduleTask
        self.table_schema = ScheduleTaskSchema
        self.origin_schedule_task = None
        self.schedule_plan_orm = {
            'date': DatePlan,
            'interval': IntervalPlan,
            'crontab': CrontabPlan
        }

    def handle_filter(self):
        for key, val in self.form_data.items():
            if hasattr(self.table_orm, key):
                self.filter.append(getattr(self.table_orm, key) == val)
            elif key == 'start_time' and val:
                self.filter.append(self.table_orm.start_time >= val)
            elif key == 'end_time' and val:
                self.filter.append(self.table_orm.end_time <= val)

    def handle_request_params(self):
        if request.method in ['PUT', 'DELETE']:
            obj = db.session.query(self.table_orm).filter(self.table_orm.id == self.table_id).first()
            self.origin_schedule_task = self.table_schema().dump(obj)
        elif request.method == 'POST':
            schedule_plan = self.form_data.get('schedule_plan')
            if self.form_data.get('trigger') == 'interval':
                self.form_data['schedule_plan'] = self.schedule_utils.handle_interval_schedule_plan(schedule_plan)

    def get_call_back(self):
        if self.form_data.get('pageNum') and self.form_data.get('pageSize'):
            for item in self.response_data.get('list'):
                schedule_plan_id = item.get('schedule_plan_id')
                schedule_task_name = item.get('schedule_task_name')
                schedule_plan = self.schedule_utils.get_schedule_plan(item.get('trigger'), schedule_plan_id)
                item['schedule_plan'] = schedule_plan
                next_run_time = self.schedule_utils.get_next_run_time(
                    item.get('trigger'), schedule_task_name, schedule_plan
                )
                item['next_run_time'] = next_run_time

        elif self.response_data and isinstance(self.response_data, dict) and request.method == 'GET':
            trigger = self.response_data.get('trigger')
            schedule_plan_id = self.response_data.get('schedule_plan_id')
            schedule_task_name = self.response_data.get('schedule_task_name')
            schedule_plan = self.schedule_utils.get_schedule_plan(trigger, schedule_plan_id)
            self.response_data['schedule_plan'] = schedule_plan
            next_run_time = self.schedule_utils.get_next_run_time(trigger, schedule_task_name, schedule_plan)
            self.response_data['next_run_time'] = next_run_time
        elif self.response_data and isinstance(self.response_data, list) and request.method == 'GET':
            for item in self.response_data:
                schedule_task_name = item.get('schedule_task_name')
                schedule_plan = self.schedule_utils.get_schedule_plan(item.get('trigger'), item.get('schedule_plan_id'))
                item['schedule_plan'] = schedule_plan
                next_run_time = self.schedule_utils.get_next_run_time(
                    item.get('trigger'), schedule_task_name, schedule_plan
                )
                item['next_run_time'] = next_run_time

    def post_call_bask(self):
        try:
            data = dict()
            self.form_data['job_id'] = self.form_data.get('schedule_task_name')
            for key, val in self.form_data.items():
                if key in CreateScheduleTask.model_fields:
                    data.setdefault(key, val)

            create_schedule_plan_param = CreateScheduleTask(**data)
            res = current_app.test_client().post(
                url_for('schedule_plan.scheduleplanview'),
                json=create_schedule_plan_param.model_dump()
            )
            if res.status_code == 200:
                schedule_plan_info = json.loads(res.data)
                schedule_plan_id = schedule_plan_info.get('data').get('table_id')
                with db.auto_commit():
                    db.session.query(self.table_orm).filter(
                        self.table_orm.id == self.table_id
                    ).update({'schedule_plan_id': schedule_plan_id})
            else:
                raise ValueError(res.data)
        except Exception as e:
            with db.auto_commit():
                db.session.query(self.table_orm).filter(
                    self.table_orm.id == self.table_id
                ).update({'active': False})
            raise ValueError(f'schedule task create error::{e}')

    def put_call_back(self):
        try:
            self.form_data['job_id'] = self.form_data.get('process_id')
            data = {'id': self.form_data.get('schedule_plan_id')}
            for key, val in self.form_data.items():
                if key in CreateScheduleTask.model_fields:
                    data.setdefault(key, val)
            create_schedule_plan_param = CreateScheduleTask(**data)
            res = current_app.test_client().put(url_for(
                'schedule_plan.scheduleplanview'),
                json=create_schedule_plan_param.model_dump()
            )
            if res.status_code == 200:
                # 修改流程
                process_info = {}
                for key, val in self.form_data.items():
                    if hasattr(Process, key):
                        process_info.setdefault(key, val)

                with db.auto_commit():
                    db.session.query(Process).filter(
                        Process.process_id == self.form_data.get('process_id')
                    ).update(process_info)
            else:
                raise ValueError(res.data)
        except Exception as e:
            if self.origin_schedule_task:
                with db.auto_commit():
                    db.session.query(self.table_orm).filter(
                        self.table_orm.id == self.table_id
                    ).update(self.origin_schedule_task)
            raise ValueError(f'schedule task update error::{e}')

    def delete_call_back(self):
        try:
            data = {
                "trigger": self.origin_schedule_task.get('trigger'),
                "id": self.origin_schedule_task.get('schedule_plan_id')
            }
            res = current_app.test_client().delete(url_for('schedule_plan.scheduleplanview'), json=data)
            if res.status_code != 200:
                raise ValueError(res.data)
        except Exception as e:
            with db.auto_commit():
                db.session.query(self.table_orm).filter(
                    self.table_orm.id == self.table_id
                ).update({'active': True})
            raise ValueError(f'schedule task delete error::{e}')

    def response_callback(self):
        if request.method == 'POST':
            self.post_call_bask()
        elif request.method == 'PUT':
            self.put_call_back()
        elif request.method == 'DELETE':
            self.delete_call_back()
        elif request.method == 'GET':
            self.get_call_back()


class ScheduleTaskReportView(BaseExportExcelView):
    """定时任务流程导出"""
    url = '/schedule_task/export'

    def __init__(self, *args, **kwargs):
        super(ScheduleTaskReportView, self).__init__(*args, **kwargs)
        self.file_name = f'schedule_task_{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}'
        self.columns = {
            'id': '序号',
            'schedule_task_name': '定时任务名称',
            'process_id': '流程id',
            'process_name': '流程名称',
            'version': '流程版本',
            'trigger': '定时类型',
            'invalid_start_time': '可用开始时间',
            'invalid_end_time': '可用结束时间',
            'next_run_time': '下次运行时间',
            'creator': '创建者',
            'updater': '修改者',
            'schedule_plan': '定时计划'
        }

    @handle_api_error()
    @orderlines_ns.doc(description='导出定时任务')
    @orderlines_ns.expect(schedule_task_marshal.list_request(schedule_task))
    def post(self):
        """导出定时任务"""
        return super(ScheduleTaskReportView, self).post()
